package com.qtrue.thread;

/*
 *  [通过使用同步代码块，解决继承Thread类的方式所出现的线程安全问题]
 *      例子：创建四个窗口卖票，总票数为100张。（使用继承于Thread类的方式实现）
 *
 *      1. new任意类作为同步锁，但需要使用static修饰这个对象，使得多个线程共用同一个锁。
 *      2. 通过反射 当前线程类名称.class 作为线程同步锁，看情况使用。
 *
 */
public class WindowTest03 {
    public static void main(String[] args) {
        // 创建4个window对象，用来启动四个线程
        Window window01 = new Window();
        Window window02 = new Window();
        Window window03 = new Window();
        Window window04 = new Window();

        // 设置线程名称
        window01.setName("窗口一：");
        window02.setName("窗口二：");
        window03.setName("窗口三：");
        window04.setName("窗口四：");

        // 启动线程
        window01.start();
        window02.start();
        window03.start();
        window04.start();
    }
}

class Window extends Thread {
    // 总票数
    /*
        使用static修饰：
            1. 编译时由内存分配一块空间，直到程序停止运行才会释放。所有对象共享这块内存；
            2. 无论哪一个方法修改了此变量，此变量都会改变；
            3. 该类的所有对象都会共享这块内存空间。
    */
    private static int ticket = 100;
//    private static Object obj = new Object(); // 使用static修饰，使得多个线程共用同一把锁，否则将出现每new一次线程类时，都new出新的对象作为锁。

    @Override
    public void run() {
        while (true) {
//            synchronized(obj){ // 方式一
            synchronized(Window.class) { // 方式二：将当前类作为同步监视器
                if(ticket > 0){
                    try {
                        // 线程休眠（挂起）
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    System.out.println(Thread.currentThread().getName() + "：卖票，票号为：" + ticket);

                    ticket--;
                }else {
                    break;
                }
            }
        }
    }
}
